/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * File Name          : freertos.c
  * Description        : Code for freertos applications
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2025 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */

/* Includes ------------------------------------------------------------------*/
#include "FreeRTOS.h"
#include "task.h"
#include "main.h"
#include "cmsis_os.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */

/* HAL */
#include "spi.h"
#include "i2c.h"
#include "rtc.h"
#include "tim.h"

/* BSP */
#include "lcd_169_st7789v2.h" 	//屏幕
#include "lcd_169_cst816s.h" 	//触摸
#include "bsp_mpu6050_dmp.h" 	//姿态
#include "max30102.h"			//心率
#include "algorithm.h"			//心率计算
#include "mx22a.h"				//蓝牙
#include "bt_api.h"				//蓝牙接口

/* LVGL */
#include "lvgl.h"
#include "lv_port_disp_template.h"
#include "lv_port_indev_template.h"
#include "ui.h"
#include "vars.h"
#include "actions.h"
#include "fonts.h"



/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */



/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN Variables */

char str_buf[128]="";

uint32_t aun_ir_buffer[500]; //IR LED sensor data
int32_t n_ir_buffer_length;    //data length
uint32_t aun_red_buffer[500];    //Red LED sensor data
int32_t n_sp02; //SPO2 value
int8_t ch_spo2_valid;   //indicator to show if the SP02 calculation is valid
int32_t n_heart_rate;   //heart rate value
int8_t  ch_hr_valid;    //indicator to show if the heart rate calculation is valid

volatile uint8_t myFlags = 0;
uint8_t hr_cnt = 0;
uint16_t sum_hr=0, sum_spo2=0;
float glb_pitch = 0, glb_roll = 0, glb_yaw = 0;

SensorData sensordata = {0};

int32_t cd_sec = 0;

uint32_t sw_10msec = 0;

uint32_t main_remain = -1, mpu_remain = -1, max_remain = -1, lv_remain = -1, rtc_remain = -1, bt_remain = -1;

/* USER CODE END Variables */
/* Definitions for MAIN_Task */
osThreadId_t MAIN_TaskHandle;
const osThreadAttr_t MAIN_Task_attributes = {
  .name = "MAIN_Task",
  .stack_size = 410 * 4,
  .priority = (osPriority_t) osPriorityHigh,
};
/* Definitions for MPU_Task */
osThreadId_t MPU_TaskHandle;
const osThreadAttr_t MPU_Task_attributes = {
  .name = "MPU_Task",
  .stack_size = 187 * 4,
  .priority = (osPriority_t) osPriorityLow,
};
/* Definitions for MAX30102_Task */
osThreadId_t MAX30102_TaskHandle;
const osThreadAttr_t MAX30102_Task_attributes = {
  .name = "MAX30102_Task",
  .stack_size = 440 * 4,
  .priority = (osPriority_t) osPriorityLow,
};
/* Definitions for RTC_Task */
osThreadId_t RTC_TaskHandle;
const osThreadAttr_t RTC_Task_attributes = {
  .name = "RTC_Task",
  .stack_size = 138 * 4,
  .priority = (osPriority_t) osPriorityLow,
};
/* Definitions for LV_Timer_Task */
osThreadId_t LV_Timer_TaskHandle;
const osThreadAttr_t LV_Timer_Task_attributes = {
  .name = "LV_Timer_Task",
  .stack_size = 1063 * 4,
  .priority = (osPriority_t) osPriorityLow,
};
/* Definitions for BT_Com_Task */
osThreadId_t BT_Com_TaskHandle;
const osThreadAttr_t BT_Com_Task_attributes = {
  .name = "BT_Com_Task",
  .stack_size = 545 * 4,
  .priority = (osPriority_t) osPriorityLow,
};
/* Definitions for BtComRxQueue */
osMessageQueueId_t BtComRxQueueHandle;
const osMessageQueueAttr_t BtComRxQueue_attributes = {
  .name = "BtComRxQueue"
};
/* Definitions for BtResRxQueue */
osMessageQueueId_t BtResRxQueueHandle;
const osMessageQueueAttr_t BtResRxQueue_attributes = {
  .name = "BtResRxQueue"
};

/* Private function prototypes -----------------------------------------------*/
/* USER CODE BEGIN FunctionPrototypes */
void cd_pause();
void mySysInit();
/* USER CODE END FunctionPrototypes */

void main_task(void *argument);
void mpu_task(void *argument);
void max30102_task(void *argument);
void rtc_task(void *argument);
void lv_timer_task(void *argument);
void bt_com_task(void *argument);

void MX_FREERTOS_Init(void); /* (MISRA C 2004 rule 8.1) */

/* Hook prototypes */
void vApplicationIdleHook(void);
void vApplicationTickHook(void);
void vApplicationStackOverflowHook(xTaskHandle xTask, signed char *pcTaskName);

/* USER CODE BEGIN 2 */
void vApplicationIdleHook( void )
{
   /* vApplicationIdleHook() will only be called if configUSE_IDLE_HOOK is set
   to 1 in FreeRTOSConfig.h. It will be called on each iteration of the idle
   task. It is essential that code added to this hook function never attempts
   to block in any way (for example, call xQueueReceive() with a block time
   specified, or call vTaskDelay()). If the application makes use of the
   vTaskDelete() API function (as this demo application does) then it is also
   important that vApplicationIdleHook() is permitted to return to its calling
   function, because it is the responsibility of the idle task to clean up
   memory allocated by the kernel to any task that has since been deleted. */
}
/* USER CODE END 2 */

/* USER CODE BEGIN 3 */
void vApplicationTickHook( void )
{
   /* This function will be called by each tick interrupt if
   configUSE_TICK_HOOK is set to 1 in FreeRTOSConfig.h. User code can be
   added here, but the tick hook is called from an interrupt context, so
   code must not attempt to block, and only the interrupt safe FreeRTOS API
   functions can be used (those that end in FromISR()). */

	lv_tick_inc(1);
}
/* USER CODE END 3 */

/* USER CODE BEGIN 4 */
void vApplicationStackOverflowHook(xTaskHandle xTask, signed char *pcTaskName)
{
   /* Run time stack overflow checking is performed if
   configCHECK_FOR_STACK_OVERFLOW is defined to 1 or 2. This hook function is
   called if a stack overflow is detected. */
	printf("！！！栈溢出！！！\n%s\n", pcTaskName);
	if(xTask == MAIN_TaskHandle)while(1);
	if(xTask == MPU_TaskHandle)while(1);
	if(xTask == MAX30102_TaskHandle)while(1);
	if(xTask == RTC_TaskHandle)while(1);
	if(xTask == LV_Timer_TaskHandle)while(1);
	if(xTask == BT_Com_TaskHandle)while(1);

}
/* USER CODE END 4 */

/**
  * @brief  FreeRTOS initialization
  * @param  None
  * @retval None
  */
void MX_FREERTOS_Init(void) {
  /* USER CODE BEGIN Init */

	// 关闭蓝牙的DMA的“传输一半”中断
	HAL_UARTEx_ReceiveToIdle_DMA(&BT_UART_HANDLE, bt_get_rx_dma_buffer(), RX_BUF_SIZE);
	__HAL_DMA_DISABLE_IT(BT_UART_HANDLE.hdmarx, DMA_IT_HT);

	// FLAG初始化
	SET_FLAG(FLAG_BIT_sw_stop_state, 1); //秒表一开始就是停止状态
	SET_FLAG(FLAG_BIT_max_mpu_canSchedule, 1); //心率和MPU一开始就可以切换

  /* USER CODE END Init */

  /* USER CODE BEGIN RTOS_MUTEX */
  /* add mutexes, ... */
  /* USER CODE END RTOS_MUTEX */

  /* USER CODE BEGIN RTOS_SEMAPHORES */
  /* add semaphores, ... */
  /* USER CODE END RTOS_SEMAPHORES */

  /* USER CODE BEGIN RTOS_TIMERS */
  /* start timers, add new ones, ... */
  /* USER CODE END RTOS_TIMERS */

  /* Create the queue(s) */
  /* creation of BtComRxQueue */
  BtComRxQueueHandle = osMessageQueueNew (10, sizeof(rx_struct), &BtComRxQueue_attributes);

  /* creation of BtResRxQueue */
  BtResRxQueueHandle = osMessageQueueNew (10, sizeof(rx_struct), &BtResRxQueue_attributes);

  /* USER CODE BEGIN RTOS_QUEUES */
  /* add queues, ... */
  /* USER CODE END RTOS_QUEUES */

  /* Create the thread(s) */
  /* creation of MAIN_Task */
  MAIN_TaskHandle = osThreadNew(main_task, NULL, &MAIN_Task_attributes);

  /* creation of MPU_Task */
  MPU_TaskHandle = osThreadNew(mpu_task, NULL, &MPU_Task_attributes);

  /* creation of MAX30102_Task */
  MAX30102_TaskHandle = osThreadNew(max30102_task, NULL, &MAX30102_Task_attributes);

  /* creation of RTC_Task */
  RTC_TaskHandle = osThreadNew(rtc_task, NULL, &RTC_Task_attributes);

  /* creation of LV_Timer_Task */
  LV_Timer_TaskHandle = osThreadNew(lv_timer_task, NULL, &LV_Timer_Task_attributes);

  /* creation of BT_Com_Task */
  BT_Com_TaskHandle = osThreadNew(bt_com_task, NULL, &BT_Com_Task_attributes);

  /* USER CODE BEGIN RTOS_THREADS */
  /* add threads, ... */
  /* USER CODE END RTOS_THREADS */

  /* USER CODE BEGIN RTOS_EVENTS */

  osThreadSuspend(MAX30102_TaskHandle); //挂起心率检测
  osThreadSuspend(BT_Com_TaskHandle); //挂起蓝牙通信

  /* USER CODE END RTOS_EVENTS */

}

/* USER CODE BEGIN Header_main_task */
/**
  * @brief  Function implementing the MAIN_Task thread.
  * @param  argument: Not used
  * @retval None
  */
/* USER CODE END Header_main_task */
void main_task(void *argument)
{
  /* USER CODE BEGIN main_task */
	//初始化
	lv_init();
	lv_port_disp_init();
	lv_port_indev_init();
	ui_init();

	mpu_dmp_init();		//姿态传感器
	max30102_init();	//心率模块

	HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1);
//	LCD_BL_ON //打开背光

	mySysInit();
	printf("系统初始化\n");

	uint32_t main_task_timestamp = 0;

	uint32_t remain = 0;

  /* Infinite loop */
  for(;;)
  {
	  //秒表 10ms
	  if(!GET_FLAG(FLAG_BIT_sw_stop_state)){
		  sw_10msec++;
		  sprintf(str_buf, "%02u : %02u .%02u", sw_10msec/100/60, sw_10msec/100%60, sw_10msec%100);
		  set_var_sw_min_sec_str(str_buf);
		  if(sw_10msec >= 360000){ //如果秒表计时大于等于1h
			  set_var_sw_min_sec_str("");
			  SET_FLAG(FLAG_BIT_sw_stop_state, 1);
		  }
	  }

	  //倒计时 100*10ms
	  if(GET_FLAG(FLAG_BIT_cd_running) && !(main_task_timestamp % 100)){
		  cd_sec --;
		  if(cd_sec % 60 == 59)set_var_cd_value(cd_sec / 60);
		  snprintf(str_buf, sizeof(str_buf), "%02d : %02d", cd_sec/60, cd_sec%60);
		  set_var_cd_value_str(str_buf);

		  if(cd_sec <= 0){
			  cd_pause();
		  }
	  }

	  //测试灯 50*10ms
	  if(!(main_task_timestamp % 50)){
		  HAL_GPIO_TogglePin(LCD_TEST_GPIO_Port, LCD_TEST_Pin);
		  // 查看姿态传感器数据 和 各任务最低剩余堆大小
//		  snprintf(str_buf, sizeof(str_buf), "pitch: %.2f\n\nroll: %.2f\n\nyaw: %.2f\nmain: %u,lv: %u,rtc: %u\n,bt: %u,mpu: %u,max: %u\n",
//				  glb_pitch, glb_roll, glb_yaw, main_remain, lv_remain, rtc_remain, bt_remain, mpu_remain, max_remain);
//		  set_var_record_str(str_buf);

		  // 更新“记录”的内容
		  snprintf(str_buf, sizeof(str_buf), "heart rate: %d bmp\n\nblood oxygen: %d%%\n\nsteps: %d\n\npitch: %.2f\n\nroll: %.2f\n\nyaw: %.2f\n",
				  sensordata.heart_rate, sensordata.blood_oxygen, sensordata.steps, glb_pitch, glb_roll, glb_yaw);
		  set_var_record_str(str_buf);
	  }
//	  remain = uxTaskGetStackHighWaterMark(NULL);
//	  if(remain < main_remain)main_remain = remain;
	  osDelay(10); //时间基准为10ms
	  main_task_timestamp++;

//	  SET_FLAG(FLAG_BIT_blt_online_state, bt_get_current_info()->connect_flag);
  }
  /* USER CODE END main_task */
}

/* USER CODE BEGIN Header_mpu_task */
/**
* @brief Function implementing the MPU_Task thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_mpu_task */
void mpu_task(void *argument)
{
  /* USER CODE BEGIN mpu_task */

	float pitch = 0,roll = 0,yaw = 0;
	unsigned long timestamp;
	uint32_t steps = 0;

	uint32_t remain = 0;

//	int state = -1;
//	uint8_t count = 0;
//	while(1){
//
//		if(count >= 3){
//			//osMutexAcquire(iic3MutexHandle, osWaitForever);
//			I2C_DEADLOCK_Handler();
//			//osMutexRelease(iic3MutexHandle);
//		}
//
//		//osMutexAcquire(iic3MutexHandle, osWaitForever);
//		state = mpu_dmp_init();
//		//osMutexRelease(iic3MutexHandle);
//
//		if(state == 10) count++;
//		if(state == 0) break;
//	}

	while(1){

		SET_FLAG(FLAG_BIT_max_mpu_canSchedule, 0);

		if(!mpu_dmp_get_data(&pitch, &roll, &yaw, &timestamp)){ //获取四元数据
			SET_FLAG(FLAG_BIT_max_mpu_canSchedule, 1);
			glb_pitch = pitch; glb_roll = roll; glb_yaw = yaw;
		}

		if(!dmp_get_pedometer_step_count(&steps)){ //获取步数
			SET_FLAG(FLAG_BIT_max_mpu_canSchedule, 1);
			sensordata.steps = (uint32_t)steps;
			sprintf(str_buf, "%d", steps);
			set_var_steps(str_buf);
		}

		SET_FLAG(FLAG_BIT_max_mpu_canSchedule, 1);

		osDelay(10);

//		  remain = uxTaskGetStackHighWaterMark(NULL);
//		  if(remain < mpu_remain)mpu_remain = remain;
	}

  /* USER CODE END mpu_task */
}

/* USER CODE BEGIN Header_max30102_task */
/**
* @brief Function implementing the MAX30102_Task thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_max30102_task */
void max30102_task(void *argument)
{
  /* USER CODE BEGIN max30102_task */

	uint8_t temp[6];
	uint16_t i=0;

	uint32_t remain = 0;

	n_ir_buffer_length = 500; //buffer length of 100 stores 5 seconds of samples running at 100sps

	for(i=0; i < n_ir_buffer_length; i++) //读取前500个样本，并确定信号范围
	{

		while(HAL_GPIO_ReadPin(HR_INT_GPIO_Port, HR_INT_Pin)) //等待中断信号
		max30102_fifo_get_data(temp);

		aun_red_buffer[i] = (long)((long)((long)temp[0] & 0x03)<<16) | (long)temp[1]<<8 | (long)temp[2];    // Combine values to get the actual number
		aun_ir_buffer[i]  = (long)((long)((long)temp[3] & 0x03)<<16) | (long)temp[4]<<8 | (long)temp[5];   // Combine values to get the actual number
	}
	//calculate heart rate and SpO2 after first 500 samples (first 5 seconds of samples)
	maxim_heart_rate_and_oxygen_saturation(aun_ir_buffer, n_ir_buffer_length, aun_red_buffer, &n_sp02, &ch_spo2_valid, &n_heart_rate, &ch_hr_valid);

  /* Infinite loop */
  for(;;)
  {
	  SET_FLAG(FLAG_BIT_max_mpu_canSchedule, 0);

	  //dumping the first 100 sets of samples in the memory and shift the last 400 sets of samples to the top
	  for(i=100; i<500; i++)
	  {
		  aun_red_buffer[i-100]=aun_red_buffer[i];
		  aun_ir_buffer[i-100]=aun_ir_buffer[i];
	  }
	  //take 100 sets of samples before calculating the heart rate.
	  for(i=400; i<500; i++)
	  {

		  while(HAL_GPIO_ReadPin(HR_INT_GPIO_Port, HR_INT_Pin));
		  max30102_fifo_get_data(temp);

		  aun_red_buffer[i] =  (long)((long)((long)temp[0]&0x03)<<16) | (long)temp[1]<<8 | (long)temp[2];    // Combine values to get the actual number
		  aun_ir_buffer[i]  = (long)((long)((long)temp[3] & 0x03)<<16) | (long)temp[4]<<8 | (long)temp[5];   // Combine values to get the actual number

	  }
	  maxim_heart_rate_and_oxygen_saturation(aun_ir_buffer, n_ir_buffer_length, aun_red_buffer, &n_sp02, &ch_spo2_valid, &n_heart_rate, &ch_hr_valid);

		if(ch_hr_valid == 1 && ch_spo2_valid ==1 && n_heart_rate > 40 && n_heart_rate<150 && n_sp02 > 50 && n_sp02<101 ){ //如果数据合法
			hr_cnt++;
			sum_hr += n_heart_rate;
			sum_spo2 += n_sp02;

			sprintf(str_buf, "%d", sum_hr / hr_cnt);
			set_var_heart_rate(str_buf);

			sprintf(str_buf, "%d", sum_spo2 / hr_cnt);
			set_var_blood_oxygen(str_buf);

			if(hr_cnt >= 5){ //如果合法数据达5个，则取均值作为结果，并释放总线
				sensordata.blood_oxygen = sum_spo2 / hr_cnt;
				sensordata.heart_rate = sum_hr / hr_cnt;
				if(bt_get_current_info()->connect_flag){
					send_sensor_data(&sensordata);
				}

				SET_FLAG(FLAG_BIT_hr_running, 0);
				osThreadResume(MPU_TaskHandle);
				SET_FLAG(FLAG_BIT_max_mpu_canSchedule, 0);
				osThreadSuspend(MAX30102_TaskHandle);
			}

		}
		SET_FLAG(FLAG_BIT_max_mpu_canSchedule, 1);
		osDelay(10);

//	  remain = uxTaskGetStackHighWaterMark(NULL);
//	  if(remain < max_remain)max_remain = remain;
  }
  /* USER CODE END max30102_task */
}

/* USER CODE BEGIN Header_rtc_task */
/**
* @brief Function implementing the RTC_Task thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_rtc_task */
void rtc_task(void *argument)
{
  /* USER CODE BEGIN rtc_task */

	RTC_DateTypeDef date;
	RTC_TimeTypeDef time;

	uint32_t remain = 0;

  /* Infinite loop */
  for(;;)
  {
	HAL_RTC_GetDate(&hrtc, &date, RTC_FORMAT_BIN);

	sprintf(str_buf, "%04d", date.Year + 2000);
	set_var_date_year((const char *)str_buf); //设置年份
	sprintf(str_buf, "%02d / %02d", date.Month, date.Date);
	set_var_date_mon_day((const char *)str_buf); //设置年份
	switch(date.WeekDay){ //设置星期
		case 1: {
			set_var_data_week("周一");
			break;
		}
		case 2: {
			set_var_data_week("周二");
			break;
		}
		case 3: {
			set_var_data_week("周三");
			break;
		}
		case 4: {
			set_var_data_week("周四");
			break;
		}
		case 5: {
			set_var_data_week("周五");
			break;
		}
		case 6: {
			set_var_data_week("周六");
			break;
		}
		case 7: {
			set_var_data_week("周日");
			break;
		}
	}

	HAL_RTC_GetTime(&hrtc, &time, RTC_FORMAT_BIN);

	sprintf(str_buf, "%02d:%02d", time.Hours, time.Minutes);
	set_var_time_hour_min((const char *)str_buf); //设置状态栏时间
	sprintf(str_buf, "%02d", time.Hours);
	set_var_time_hour((const char *)str_buf); //设置首页小时
	sprintf(str_buf, "%02d", time.Minutes);
	set_var_time_min((const char *)str_buf); //设置首页分钟

	osDelay(5 * 1000); // 5秒获取一次RTC时间
//	  remain = uxTaskGetStackHighWaterMark(NULL);
//	  if(remain < rtc_remain)rtc_remain = remain;
  }
  /* USER CODE END rtc_task */
}

/* USER CODE BEGIN Header_lv_timer_task */
/**
* @brief Function implementing the LV_Timer_Task thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_lv_timer_task */
void lv_timer_task(void *argument)
{
  /* USER CODE BEGIN lv_timer_task */

	uint32_t remain = 0;
  /* Infinite loop */
  for(;;)
  {
	  ui_tick();
	  lv_timer_handler();
//	  remain = uxTaskGetStackHighWaterMark(NULL);
//	  if(remain < lv_remain)lv_remain = remain;
	  osDelay(20);
  }
  /* USER CODE END lv_timer_task */
}

/* USER CODE BEGIN Header_bt_com_task */
/**
* @brief Function implementing the BT_Com_Task thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_bt_com_task */
void bt_com_task(void *argument)
{
  /* USER CODE BEGIN bt_com_task */

	uint32_t remain = 0;

	rx_struct rx_msg;   // 接收数据结构体
    /* Infinite loop */
	while(1)
	{
		// 从蓝牙通信队列接收数据
		xQueueReceive(BT_COM_RX_QUEUE_HANDLE, &rx_msg, portMAX_DELAY);
		// 处理接收到的蓝牙命令
		handle_ble_command(rx_msg.rx_data);

		if(!GET_FLAG(FLAG_BIT_time_synced)){
			if(SendCommand(BT_TIME)){ //同步时间
				SET_FLAG(FLAG_BIT_time_synced, 1);
			}
		}
//		  remain = uxTaskGetStackHighWaterMark(NULL);
//		  if(remain < bt_remain)bt_remain = remain;
		// 打印栈剩余空间
//		printf("[bt_com_task] Stack high water mark: %lu words\r\n", uxTaskGetStackHighWaterMark(NULL)); //单位：字
	}
  /* USER CODE END bt_com_task */
}

/* Private application code --------------------------------------------------*/
/* USER CODE BEGIN Application */

void mySysInit(){
	/* 关闭蓝牙的广播 */
	send_at_command("AT+SPP=0\r\n", "OK", 10, 100);
	send_at_command("AT+BLE=0\r\n", "OK", 10, 100);
}

void action_go_to(lv_event_t * e){

	switch((int)lv_event_get_user_data(e)){
	case 1: //从“首页”进入“菜单”
		loadScreen(SCREEN_ID_MENU);
		break;

	case 111: //从“菜单”进入“健康”
		loadScreen(SCREEN_ID_HEALTH);
		break;

	case 112: //从“菜单”进入“蓝牙”
		loadScreen(SCREEN_ID_BLUETOOTH);
		break;

	case 113: //从“菜单”进入“秒表”
		loadScreen(SCREEN_ID_STOPWATCH);
		break;

	case 114: //从“菜单”进入“倒计时”
		loadScreen(SCREEN_ID_COUNTDOWN);
		break;

	case 115: //从“菜单”进入“记录”
		loadScreen(SCREEN_ID_RECORD);
		break;

	case 116: //从“菜单”进入“系统”
		loadScreen(SCREEN_ID_SYSTEM);
		break;

	default:
		loadScreen(SCREEN_ID_MAIN);

	}

}

void action_go_back(lv_event_t * e){
	switch((int)lv_event_get_user_data(e)){

	case 110: //从“菜单”返回“首页”
		loadScreen(SCREEN_ID_MAIN);
		break;

	case 210: //从“健康”返回“菜单”
		if(GET_FLAG(FLAG_BIT_hr_running)){ //如果心率模块正在运行
			while(!GET_FLAG(FLAG_BIT_max_mpu_canSchedule));
			if(MAX30102_TaskHandle) osThreadSuspend(MAX30102_TaskHandle);
			if(MPU_TaskHandle) osThreadResume(MPU_TaskHandle);
			SET_FLAG(FLAG_BIT_hr_running, 0);
		}
		loadScreen(SCREEN_ID_MENU);
		break;

	default: //默认返回“菜单”，因为返回菜单的需求最多
		loadScreen(SCREEN_ID_MENU);

	}
}

void action_run_active(lv_event_t * e){
	switch((int)lv_event_get_user_data(e)){
	case 211: //健康 “开始测量”
		while(!GET_FLAG(FLAG_BIT_max_mpu_canSchedule)); //等待MPU释放可切换信号
		SET_FLAG(FLAG_BIT_max_mpu_canSchedule, 0);
		hr_cnt = 0; sum_hr = 0; sum_spo2 = 0;
		set_var_heart_rate("---");
		set_var_blood_oxygen("---");
		if(MPU_TaskHandle) osThreadSuspend(MPU_TaskHandle);
		if(MAX30102_TaskHandle) osThreadResume(MAX30102_TaskHandle);
		SET_FLAG(FLAG_BIT_hr_running, 1);
		break;

	case 221: //“翻转”蓝牙开关状态
		if(GET_FLAG(FLAG_BIT_blt_state)){
			if(ble_module_init()){ //如果初始化成功（该函数初始化成功返回1）
				osThreadResume(BT_Com_TaskHandle);
			}
			else{
				osDelay(500);
				SET_FLAG(FLAG_BIT_blt_state, 0);
				osThreadSuspend(BT_Com_TaskHandle);
			}
		}
		else{
			if(BT_Disconnect()){
				bt_get_current_info()->connect_flag = 0;
			}
			send_at_command("AT+SPP=0\r\n", "OK", 10, 100);
			send_at_command("AT+BLE=0\r\n", "OK", 10, 100);
		}
		break;

	case 222: //蓝牙“同步时间”
		if(bt_get_current_info()->connect_flag){
			SendCommand(BT_TIME);
		}
		break;

	case 223: //蓝牙“上传数据”
		if(bt_get_current_info()->connect_flag){
			send_sensor_data(&sensordata);
		}
		break;

	case 231: //秒表 “重置”
		SET_FLAG(FLAG_BIT_sw_stop_state, 1); //暂停秒表
		sw_10msec = 0;
		set_var_sw_min_sec_str("00 : 00 .00");
		set_var_sw_btn_str("开始");
		break;

	case 232: //秒表 “开始/暂停”
		TOGGLE_FLAG(FLAG_BIT_sw_stop_state);
		if(GET_FLAG(FLAG_BIT_sw_stop_state)){ //秒表暂停
			set_var_sw_btn_str("开始");
		}
		else{ //秒表开始
			set_var_sw_btn_str("暂停");
		}
		break;

	case 242: //倒计时 “开始/暂停”
		TOGGLE_FLAG(FLAG_BIT_cd_running); //翻转FLAG_BIT_cd_running的状态
		if(GET_FLAG(FLAG_BIT_cd_running)){ //开始倒计时
			if(cd_sec > 0){ //倒计时大于0
				set_var_cd_btn_str("暂停");
				lv_lock();
				lv_obj_set_style_opa(objects.obj0, LV_OPA_TRANSP, LV_PART_KNOB); //隐藏Arc的圆点
				lv_unlock();
			}
			else{
				SET_FLAG(FLAG_BIT_cd_running, 0);
			}
		}
		else{ //暂停倒计时
			cd_pause();
		}
		break;

	default:
		while(1);
	}
}

void cd_pause(){
	set_var_cd_btn_str("开始");
	lv_lock();
	lv_obj_set_style_opa(objects.obj0, LV_OPA_COVER, LV_PART_KNOB); //隐藏Arc的圆点
	lv_unlock();
  	SET_FLAG(FLAG_BIT_cd_running, 0);
}

void action_on_arc_value_changed(lv_event_t * e){
	uint8_t arc_value = lv_arc_get_value(lv_event_get_target(e));
	switch((int)lv_event_get_user_data(e)){
	case 241: //滑动“倒计时”的进度条
		if(!GET_FLAG(FLAG_BIT_cd_running)){
			cd_sec = arc_value * 60;
			snprintf(str_buf, sizeof(str_buf), "%02d : 00", arc_value);
			set_var_cd_value_str(str_buf);
		}
		break;

	default:
		while(1);
	}
}

void action_on_slider_value_changed(lv_event_t * e){
	uint8_t slider_value = lv_slider_get_value(lv_event_get_target(e));
	switch((int)lv_event_get_user_data(e)){
	case 261: //“系统”中，滑动亮度条
		__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, slider_value);
		break;

	default:
		while(1);
	}
}

/* USER CODE END Application */

